using System;
using System.Collections;
using System.Reflection;

using Deepcode.Flux.Core.Systems.Workflow.Attribs;
using Deepcode.Flux.Core.Systems.Workflow.BaseHandlers;
using Deepcode.Flux.Core.Systems.Workflow.Bases;
using Deepcode.Flux.Core.Systems.Workflow.Enums;
using Deepcode.Flux.Core.Systems.Workflow.Info;
using Deepcode.Flux.Core.Systems.Workflow.Interfaces;

namespace Deepcode.Flux.Core.Systems.Workflow
{
	/// <summary>
	/// This is the main class for processing workflow. It hooks into the specified
	/// handler and invokes the actions etc for it.
	/// </summary>
	public class WorkflowEngine
	{
		#region Private Fields
		/// <summary>
		/// This field holds a Handler record
		/// </summary>
		private Handler MyHandler = null;
		private Type HandlerType = null;
		private IWorkflowHandler Handler = null;
		#endregion
		#region Construction
		/// <summary>
		/// Constructor, wraps the workflow engine around the 
		/// workflow handler passed in by it's unique ID
		/// </summary>
		/// <param name="HandlerID"></param>
		public WorkflowEngine( string HandlerCodeID )
		{
			Handlers handlers = new Handlers();
			MyHandler = handlers.GetHandlerByID( HandlerCodeID );
			if( MyHandler == null ) throw new NullReferenceException("Construction of Workflow Engine failed - HandlerID (" + HandlerCodeID + ") couldn't be found");
			CreateIWorkflow();
		}
		#endregion
		#region Object creation
		/// <summary>
		/// Creates an IWorkflowHandler from the MyHandler.HandlerClass string
		/// </summary>
		private void CreateIWorkflow()
		{
			HandlerType = Type.GetType( MyHandler.Class, true, true );
			Handler = (IWorkflowHandler) HandlerType.GetConstructor( System.Type.EmptyTypes ).Invoke( null );
		}
		#endregion
		#region Object Query
		/// <summary>
		/// Retrieves a list of all UI methods in the handler.
		/// </summary>
		/// <returns></returns>
		public WorkflowUIMethodInfos GetWorkflowUIMethods()
		{
			WorkflowUIMethodInfos result = new WorkflowUIMethodInfos();

			MethodInfo [] methods = HandlerType.GetMethods();
			foreach( MethodInfo method in methods )
			{
				// Obtain the attributes
				WorkflowMethodUI WMU = (WorkflowMethodUI) Attribute.GetCustomAttribute( method, typeof( WorkflowMethodUI ));
				if( WMU != null )
				{
					WorkflowUIMethodInfo info = new WorkflowUIMethodInfo(method.Name, WMU.FriendlyMethodName);
					result.Add( info );
				}
			}
			return result;
		}
		/// <summary>
		/// Returns the WorkflowUIMethodInfo descriptor for the specified method
		/// If the method doesn't exist, or isn't a WorkflowUIMethod, this will return null.
		/// </summary>
		/// <param name="Method"></param>
		/// <returns></returns>
		public WorkflowUIMethodInfo GetWorkflowUIMethodInfo( string Method )
		{
			MethodInfo [] methods = HandlerType.GetMethods();
			foreach( MethodInfo method in methods )
			{
				if( method.Name == Method )
				{
					WorkflowMethodUI WMU = (WorkflowMethodUI) Attribute.GetCustomAttribute( method, typeof( WorkflowMethodUI ));
					if( WMU != null )
					{
						WorkflowUIMethodInfo info = new WorkflowUIMethodInfo( method.Name, WMU.FriendlyMethodName );
						return info;
					}
					return null;
				}
			}
			return null;
		}
		/// <summary>
		/// Retrieves a list of all taskable methods in the handler.
		/// </summary>
		/// <returns></returns>
		public WorkflowTaskableMethodInfos GetWorkflowTaskableMethods()
		{
			WorkflowTaskableMethodInfos result = new WorkflowTaskableMethodInfos();

			MethodInfo [] methods = HandlerType.GetMethods();
			foreach( MethodInfo method in methods )
			{
				// Obtain the attributes
				WorkflowTaskableMethod WFTM = (WorkflowTaskableMethod) Attribute.GetCustomAttribute( method, typeof( WorkflowTaskableMethod ));
				if( WFTM != null )
				{
					WorkflowTaskableMethodInfo info = new WorkflowTaskableMethodInfo( method.Name, WFTM.FriendlyMethodName );
					result.Add( info );
				}
			}
			return result;
		}
		/// <summary>
		/// Returns the WorkflowTaskableMethodInfo descriptor for the method name specified.
		/// Returns null if the method either couldn't be found, or the method isn't marked with
		/// the WorkflowTaskableMethod attribute.
		/// </summary>
		/// <param name="Method"></param>
		/// <returns></returns>
		public WorkflowTaskableMethodInfo GetWorkflowTaskableMethodInfo( string Method )
		{
			MethodInfo [] methods = HandlerType.GetMethods();
			foreach( MethodInfo method in methods )
			{
				if( method.Name == Method )
				{
					WorkflowTaskableMethod WFTM = (WorkflowTaskableMethod) Attribute.GetCustomAttribute( method, typeof( WorkflowTaskableMethod ));
					if( WFTM != null )
					{
						WorkflowTaskableMethodInfo info = new WorkflowTaskableMethodInfo( method.Name, WFTM.FriendlyMethodName );
						return info;
					}
					return null;
				}
			}
			return null;
		}
		/// <summary>
		/// Returns a WorkflowMethodInfos collection of workflow methods available in the workflow handler class
		/// </summary>
		/// <returns></returns>
		public WorkflowMethodInfos GetWorkflowMethods()
		{
			WorkflowMethodInfos result = new WorkflowMethodInfos();

			MethodInfo [] methods = HandlerType.GetMethods();
			foreach( MethodInfo method in methods )
			{
				// Attempt to obtain the different attributes we have available
				WorkflowMethod WFM = (WorkflowMethod) Attribute.GetCustomAttribute(method, typeof( WorkflowMethod ));
				if( WFM != null )
				{
					WorkflowMethodInfo info = new WorkflowMethodInfo( method.Name, WFM.FriendlyMethodName, WFM.ConfigurationClassName );
					result.Add( info );
				}
			}
			return result;
		}
		/// <summary>
		/// Returns a WorkflowMethodInfo object for the method name specified
		/// </summary>
		/// <param name="Method"></param>
		/// <returns></returns>
		public WorkflowMethodInfo GetWorkflowMethodInfo( string Method )
		{
			MethodInfo [] methods = HandlerType.GetMethods();
			foreach( MethodInfo method in methods )
			{
				if( method.Name == Method )
				{
					WorkflowMethod WFM = (WorkflowMethod) Attribute.GetCustomAttribute(method, typeof( WorkflowMethod ));
					if( WFM != null )
					{
						WorkflowMethodInfo info = new WorkflowMethodInfo( method.Name, WFM.FriendlyMethodName, WFM.ConfigurationClassName );
						return info;
					}
					return null;
				}
			}
			return null;
		}
		#endregion
		#region IWorkflowHandler mappings
		/// <summary>
		/// Maps onto the IWorkflowHandler CanBeEdited function
		/// </summary>
		/// <param name="DefinitionID"></param>
		/// <returns></returns>
		public bool CanBeEdited( int DefinitionID )
		{
			return Handler.CanBeEdited( DefinitionID );
		}
		/// <summary>
		/// Maps onto the IWorklowHandler CanBeDeleted function
		/// </summary>
		/// <param name="DefinitionID"></param>
		/// <returns></returns>
		public bool CanBeDeleted( int DefinitionID )
		{
			return Handler.CanBeDeleted( DefinitionID );
		}
		/// <summary>
		/// Returns a list of taskable operators for the handler. 
		/// Maps onto the IWorkflowHandler GetTaskableOperators function
		/// </summary>
		/// <returns></returns>
		public WorkflowTaskableOperators GetTaskableOperators()
		{
			return Handler.GetTaskableOperators();
		}
		/// <summary>
		/// Returns a list of taskable groups for the handler.
		/// Maps onto the IWorkflowHandler GetTaskableGroups function
		/// </summary>
		/// <returns></returns>
		public WorkflowTaskableGroups GetTaskableGroups()
		{
			return Handler.GetTaskableGroups();
		}
		/// <summary>
		/// Ask the engine to find the group as specified by ID and
		/// return a workflow taskable group object from it.
		/// </summary>
		/// <param name="GroupID"></param>
		/// <returns></returns>
		public WorkflowTaskableGroup GetTaskableGroup( int GroupID )
		{
			return Handler.GetTaskableGroup( GroupID );
		}
		/// <summary>
		/// Ask the engine to find the operator as specified by ID and
		/// return a workflow taskable operator object from it.
		/// </summary>
		/// <param name="OperatorID"></param>
		/// <returns></returns>
		public WorkflowTaskableOperator GetTaskableOperator( int OperatorID )
		{
			return Handler.GetTaskableOperator( OperatorID );
		}
		/// <summary>
		/// Pass through to the handler the fact that a workflow definition is being deleted
		/// </summary>
		/// <param name="DefinitionID"></param>
		public void OnDeleteDefinition( int DefinitionID )
		{
			Handler.OnDeleteWorkflowDefinition( DefinitionID );
		}
		/// <summary>
		/// Asks the handler for the value of the data dictionary item (Key), against
		/// the data item (DataID)
		/// </summary>
		/// <param name="Key"></param>
		/// <param name="DataID"></param>
		/// <returns></returns>
		public string DataDictionaryItem( string Key, int DataID )
		{
			return Handler.DataDictionaryItem( Key, DataID );
		}
		/// <summary>
		/// Return information concerning what information is exposed from the 
		/// object into the workflow.
		/// </summary>
		/// <returns></returns>
		public WorkflowDataDictionaryDescriptor GetDataDictionary()
		{
			return Handler.GetDataDictionary();
		}
		#endregion
		#region Method Configuration Actions
		/// <summary>
		/// Returns an IWorkflowHandlerMethodConfig object for the method specified
		/// </summary>
		/// <param name="Method"></param>
		/// <returns></returns>
		public IWorkflowHandlerMethodConfig GetConfigObjectForMethod( string Method )
		{
			WorkflowMethodInfo method = GetWorkflowMethodInfo( Method );
			if( method == null ) return null;
			if( ! method.HasConfig ) return null;
			if( method.ConfigClass == "" ) return null;

			Type ConfigType = Type.GetType( method.ConfigClass );
			if( ConfigType == null ) return null;
			object obj = ConfigType.GetConstructor(System.Type.EmptyTypes).Invoke( null );
			if( obj == null ) return null;
			if( !(obj is IWorkflowHandlerMethodConfig)) return null;
			return (IWorkflowHandlerMethodConfig) obj;
		}
		#endregion
		#region Processing Instructions
		/// <summary>
		/// Stops any outstanding processing for the DataID specified.
		/// </summary>
		/// <param name="DataID"></param>
		public void ClearProcessingFor( int DataID )
		{
			Tasks tasks = new Tasks();
			tasks.DeleteByHandlerAndDataID( MyHandler.ID, DataID );
		}
		/// <summary>
		/// Starts processing a workflow from the very beginning
		/// </summary>
		/// <param name="WorkflowDefinitionID"></param>
		/// <param name="DataID"></param>
		/// <param name="DataName"></param>
		public void BeginProcessing( int WorkflowDefinitionID, int DataID, string DataName )
		{
			// Get the lowest rank process in the definition
			Processes procs = new Processes();
			Process process = procs.GetStartProcessForDefinitionID( WorkflowDefinitionID );
			if( process == null ) return;
			// Start the process
			InvokeProcess( process.PK_ID, DataID, DataName );
		}
		/// <summary>
		/// Starts the specified process against the specified data item
		/// This in turn then loads the process and interrogates it's type
		/// If the type is CLEAR - any current tasks outstanding against the data are cleared
		/// If the type is WAIT, the function inspects the tasks list agains the data. If any non system tasks 
		/// are still outstanding, this function returns. If oustanding tasks are ONLY system tasks, then this function
		/// will clear those down, regarless of CLEAR/WAIT status.
		/// 
		/// Next, the function interrogates what startup actions should be invoked, and it invokes them.
		/// And finally, the taskings are examined and new tasks created to continue processing (if any)
		/// </summary>
		/// <param name="ProcessID"></param>
		/// <param name="DataID"></param>
		/// <param name="DataName"></param>
		public void InvokeProcess( int ProcessID, int DataID, string DataName )
		{
			// Loads the process
			Processes procs = new Processes();
			Process process = procs.GetProcessByID( ProcessID );
			if( process == null ) return;
			// If process is WAIT - query TASKS for !ForSystem any exist ? do nothing
			//		Delete any outstanding tasks for the dataid
			// If process is CLEAR, Delete any outstanding tasks for the dataid
			if( ((WorkflowProcessType) process.ProcessType) == WorkflowProcessType.PROCESS_WAIT )
			{
				// Query for non system tasks
				Tasks tasks = new Tasks();
				if( tasks.GetNonSystemTasksFor( MyHandler.ID, DataID ).Length > 0 ) return;
			}
			// Clear down any outstanding tasking
			ClearProcessingFor( DataID );		
			// Raise all of the taskings
			Taskings taskings = new Taskings();
			Tasking [] Taskings = taskings.GetTaskingsByProcessID( ProcessID );
			// Create all WFTasks entrys
			foreach( Tasking Tasking in Taskings )
			{
				if( Tasking.SystemTask )
					CreateSystemTasksFrom( Tasking, process, DataID, DataName );
				else
				{
					// Examine individual operators
					if( Tasking.CSV_OperatorID != "" )
					{
						// We have a potential list of operator ID's
						string [] OperatorIDList = Tasking.CSV_OperatorID.Split(',');
						foreach( string SOperatorID in OperatorIDList )
						{
							int OperatorID = -1;
							try{ OperatorID = Int32.Parse( SOperatorID ); } 
							catch{ OperatorID = -1; }
							if( OperatorID != -1 )
								CreateOperatorTaskFrom( Tasking, OperatorID, process, DataID, DataName );
						}
					}
					// Examine individual groups
					if( Tasking.CSV_GroupID != "" )
					{
						// We have a potential list of group ID's
						string [] GroupIDList = Tasking.CSV_GroupID.Split(',');
						foreach( string SGroupID in GroupIDList )
						{
							int GroupID = -1;
							try{ GroupID = Int32.Parse( SGroupID ); }
							catch{ GroupID = -1; }
							if( GroupID != -1 )
								CreateGroupTaskFrom( Tasking, GroupID, process, DataID, DataName );
						}
					}
					// Get info from any assigned callbacks
					if( Tasking.GetUserMethod != "" )
					{
						int [] GroupIDs = InvokeUserMethod( UserCallbackType.REQUEST_GROUPS, Tasking.GetUserMethod, ProcessID, DataID, DataName );
						int [] UserIDs = InvokeUserMethod( UserCallbackType.REQUEST_USERS, Tasking.GetUserMethod, ProcessID, DataID, DataName );
						if( (GroupIDs != null) && (GroupIDs.Length>0) )
						{
							foreach( int GID in GroupIDs )
								CreateGroupTaskFrom( Tasking, GID, process, DataID, DataName );
						}
						if( (UserIDs != null ) && (UserIDs.Length>0) )
						{
							foreach( int UID in UserIDs )
								CreateOperatorTaskFrom( Tasking, UID, process, DataID, DataName );
						}
					}
				}
			}
			// Get all startup actions
			ProcessStarts procstarts = new ProcessStarts();
			ProcessStart [] Starts = procstarts.GetAllProcessStartForProcessID( ProcessID );
			//		Invoke each method( ProcessID, DataID, PropertyBag )
			foreach( ProcessStart Start in Starts )
			{
				// Invoke it!
				Invoke( Start.Method, ProcessID, DataID, DataName, Start.PropertyBag );
			}
		}
		/// <summary>
		/// Processes an Exit Action (Non-UI, or from the UI processor)
		/// Reads the task into memory, deletes it and any shared FK_TaskingID records against the FK_DataID.
		/// Runs through each of the exit actions in turn.
		/// 
		/// Before calling this function, you should check the ExitPathID to see if it invokes a uiTaskMethod
		/// If it does, one should redirect to the URL returned from the uiTaskMethod URL, which in turn will call this function.
		/// If it does not, then by all means, call this function
		/// </summary>
		/// <param name="TaskID"></param>
		/// <param name="ExitPathID"></param>
		public void ProcessExitAction( int TaskID, int ExitPathID )
		{
			//	Load the task
			Tasks tasks = new Tasks();
			Task Task = tasks.GetTasksByID( TaskID );
			if( Task == null ) return;
			//	Delete this task + any with the same FK_TaskingID record for this FK_DataID	
			tasks.DeleteByTaskingAndDataID( Task.FK_TaskingID, Task.FK_DataID );
			//	Execute each of the exit actions in turn
			ExitActions exitactions = new ExitActions();
			ExitAction [] exitActions = exitactions.GetExitActionsForExitPathID( ExitPathID );
			foreach( ExitAction Action in exitActions )
			{
				// Invoke each method( ProcessID, DataID, PropertyBag )
				Invoke( Action.Method, Task.FK_ProcessID, Task.FK_DataID, Task.DataItemText, Action.PropertyBag );
			}
		}
		/// <summary>
		/// Private function to invoke an action method
		/// </summary>
		/// <param name="MethodName"></param>
		/// <param name="ProcessID"></param>
		/// <param name="DataID"></param>
		/// <param name="PropertyBag"></param>
		private void Invoke( string MethodName, int ProcessID, int DataID, string DataName, string PropertyBag )
		{
			try
			{
				// Get the method
				MethodInfo Method = this.HandlerType.GetMethod( MethodName );
				if( Method == null ) return;
				// Load up the parameters
				object [] Parameters = {this, ProcessID, DataID, DataName, PropertyBag};
				// Invoke it
				Method.Invoke( this.Handler, Parameters );
			}
			catch(Exception e){ throw e; }
		}
		/// <summary>
		/// Private function to invoke a workflowTaskableMethod to return
		/// either a list of tasked operators or tasked groups.
		/// </summary>
		/// <param name="RequestType"></param>
		/// <param name="Tasking"></param>
		/// <param name="ProcessID"></param>
		/// <param name="DataID"></param>
		/// <param name="DataName"></param>
		public int [] InvokeUserMethod( UserCallbackType RequestType, string GetUserMethod, int ProcessID, int DataID, string DataName )
		{
			try
			{
				// Get the method
				MethodInfo Method = this.HandlerType.GetMethod( GetUserMethod );
				if( Method == null ) return null;
				// Load up parameters
				object [] Parameters = {this, RequestType, ProcessID, DataID, DataName };
				// Invoke it, capturing the result
				object result = Method.Invoke( this.Handler, Parameters );
				if( result == null ) return null;
				if( !(result is int[])) return null;
				return (int []) result;
			}
			catch{}
			return null;
		}
		/// <summary>
		/// Hooks into the handler to invoke a WorkflowMethodUI based on the Task and ExitPath specified
		/// </summary>
		/// <param name="TaskID"></param>
		/// <param name="ExitPathID"></param>
		/// <returns></returns>
		public string GetExitUI( string UIMethod, int TaskID, int ExitPathID )
		{
			try
			{
				// Get the method
				MethodInfo Method = this.HandlerType.GetMethod( UIMethod );
				if( Method == null ) return "";
				// Load up the parameters to pass in
				object [] Parameters = {this, TaskID, ExitPathID};
				// Invoke it, capturing result
				object result = Method.Invoke( this.Handler, Parameters );
				if( result == null ) return "";
				if( !(result is string)) return "";
				return (string) result;
			}
			catch{}
			return "";
		}
		/// <summary>
		/// Returns the URL for editing the data item by passing the data through
		/// to the workflow handler object.
		/// </summary>
		/// <param name="Task"></param>
		/// <returns></returns>
		public string GetEditURL( Task Task )
		{
			return Handler.GetEditURL( Task );
		}
		/// <summary>
		/// Returns the URL for viewing the data item by passing the data through
		/// to the workflow handler object.
		/// </summary>
		/// <param name="Task"></param>
		/// <returns></returns>
		public string GetViewURL( Task Task )
		{
			return Handler.GetViewURL( Task );
		}
		#endregion
		#region Utilities
		/// <summary>
		/// Creates a group tasking based on a tasking and process record
		/// agains the data ID and name passed in. Task raised against GroupID.
		/// </summary>
		/// <param name="Tasking"></param>
		/// <param name="OperatorID"></param>
		/// <param name="Process"></param>
		/// <param name="DataID"></param>
		/// <param name="DataName"></param>
		private void CreateGroupTaskFrom( Tasking Tasking, int GroupID, Process Process, int DataID, string DataName )
		{
			Task NewTask = new Task();
			NewTask.HandlerCodeID = Process.ParentDefinition.HandlerCodeID;
			NewTask.FK_DefinitionID = Process.ParentDefinition.PK_ID;
			NewTask.FK_ProcessID = Process.PK_ID;
			NewTask.FK_TaskingID = Tasking.PK_ID;
			NewTask.TaskCaption = Tasking.TaskCaption;
			NewTask.DataItemText = DataName;
			NewTask.FK_DataID = DataID;
			// Setup system level information
			NewTask.FK_ForOperatorGroupID = GroupID;
			NewTask.FK_ForOperatorID = -1;
			NewTask.StartDT = GetNowPlusTimescale( Tasking.ActivateTaskAt, (WorkflowTimescale) Tasking.ActivateTaskAtUnit );
			NewTask.DueDT = GetNowPlusTimescale( Tasking.DeadlineTaskAt, (WorkflowTimescale) Tasking.DeadlineTaskAtUnit );
			NewTask.ForSystem = false;
			NewTask.ForSystemExecDT = System.DateTime.Now;
			
			Tasks tasks = new Tasks();
			tasks.Save( NewTask );
		}
		/// <summary>
		/// Creates an operator tasking based on a tasking and process record
		/// agains the data ID and name passed in. Task raised against OperatorID.
		/// </summary>
		/// <param name="Tasking"></param>
		/// <param name="OperatorID"></param>
		/// <param name="Process"></param>
		/// <param name="DataID"></param>
		/// <param name="DataName"></param>
		private void CreateOperatorTaskFrom( Bases.Tasking Tasking, int OperatorID, Bases.Process Process, int DataID, string DataName )
		{
			Bases.Task NewTask = new Bases.Task();
			NewTask.HandlerCodeID = Process.ParentDefinition.HandlerCodeID;
			NewTask.FK_DefinitionID = Process.ParentDefinition.PK_ID;
			NewTask.FK_ProcessID = Process.PK_ID;
			NewTask.FK_TaskingID = Tasking.PK_ID;
			NewTask.TaskCaption = Tasking.TaskCaption;
			NewTask.DataItemText = DataName;
			NewTask.FK_DataID = DataID;
			// Setup system level information
			NewTask.FK_ForOperatorGroupID = -1;
			NewTask.FK_ForOperatorID = OperatorID;
			NewTask.StartDT = GetNowPlusTimescale( Tasking.ActivateTaskAt, (WorkflowTimescale) Tasking.ActivateTaskAtUnit );
			NewTask.DueDT = GetNowPlusTimescale( Tasking.DeadlineTaskAt, (WorkflowTimescale) Tasking.DeadlineTaskAtUnit );
			NewTask.ForSystem = false;
			NewTask.ForSystemExecDT = System.DateTime.Now;

			Tasks tasks = new Tasks();
			tasks.Save( NewTask );
		}
		/// <summary>
		/// Creates a system tasking based on a tasking and process record
		/// against the data ID and name passed in.
		/// </summary>
		/// <param name="Tasking"></param>
		/// <param name="Process"></param>
		/// <param name="DataID"></param>
		/// <param name="DataName"></param>
		private void CreateSystemTasksFrom( Bases.Tasking Tasking, Bases.Process Process, int DataID, string DataName )
		{
			Task NewTask = new Task();
			NewTask.HandlerCodeID = Process.ParentDefinition.HandlerCodeID;
			NewTask.FK_DefinitionID = Process.ParentDefinition.PK_ID;
			NewTask.FK_ProcessID = Process.PK_ID;
			NewTask.FK_TaskingID = Tasking.PK_ID;
			NewTask.TaskCaption = Tasking.TaskCaption;
			NewTask.DataItemText = DataName;
			NewTask.FK_DataID = DataID;
			// Setup system level information
			NewTask.FK_ForOperatorGroupID = -1;
			NewTask.FK_ForOperatorID = -1;
			NewTask.StartDT = System.DateTime.Now;
			NewTask.DueDT = System.DateTime.Now;
			NewTask.ForSystem = true;
			NewTask.ForSystemExecDT = GetNowPlusTimescale( Tasking.SystemExitAt, (WorkflowTimescale) Tasking.SystemExitAtUnit );
			
			Tasks tasks = new Tasks();
			tasks.Save( NewTask );
		}
		/// <summary>
		/// Returns the present date and time plus the timescale specified.
		/// </summary>
		/// <param name="NumberOfUnits"></param>
		/// <param name="Units"></param>
		/// <returns></returns>
		public System.DateTime GetNowPlusTimescale( int NumberOfUnits, WorkflowTimescale Units )
		{
			if( NumberOfUnits == 0 ) return System.DateTime.Now;
			switch( Units )
			{
				case WorkflowTimescale.YEAR:
					return System.DateTime.Now.AddYears( NumberOfUnits );
				case WorkflowTimescale.MONTH:
					return System.DateTime.Now.AddMonths( NumberOfUnits );
				case WorkflowTimescale.DAY:
					return System.DateTime.Now.AddDays( NumberOfUnits );
				case WorkflowTimescale.HOUR:
					return System.DateTime.Now.AddHours( NumberOfUnits );
				case WorkflowTimescale.MINUTE:
					return System.DateTime.Now.AddMinutes( NumberOfUnits );
			}
			return System.DateTime.Now;
		}
		#endregion
		#region Data Dictionary Processor
		/// <summary>
		/// Takes the field content and examines any [/Fieldname/] instances
		/// within the data. Any there are compared against the data dictionary
		/// and replaced if valid field names
		/// </summary>
		/// <param name="FieldContent"></param>
		/// <returns></returns>
		public string ProcessDictionaryField( string FieldContent, int DataID )
		{
			string Result = FieldContent;
			// Do quick replacements of all data dictionary fields
			WorkflowDataDictionaryDescriptor wddd = this.GetDataDictionary();
			foreach( WorkflowDataDictionary dic in wddd )
			{
				string FieldValue = this.DataDictionaryItem( dic.KeyName, DataID );
				Result = Result.Replace( "[/" + dic.KeyName + "/]", FieldValue );
			}
			return Result;
		}
		#endregion
	}
}
